/*!	 synfig/rendering/resource.cpp
**	 Resource
**
**	......... ... 2015 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#endif

#include "resource.h"

#endif

using namespace synfig;
using namespace rendering;

Resource::Id Resource::last_id = 0;

Resource::Storage::Storage(): refcount() { }

Resource::Storage::~Storage() { }

void
Resource::Storage::ref() const
{
#ifdef ETL_LOCK_REFCOUNTS
    etl::mutex::lock lock(mtx);
#endif
    ++refcount;
}

bool
Resource::Storage::unref_inactive() const
{
#ifdef ETL_LOCK_REFCOUNTS
    etl::mutex::lock lock(mtx);
#endif
    return refcount == 0 || --refcount != (int)resources.size();
}

bool
Resource::Storage::unref() const
{
#ifdef ETL_LOCK_REFCOUNTS
    etl::mutex::lock lock(mtx);
#endif

    if (refcount == 0) {
        return true;
    }

    if (--refcount == (int)resources.size()) {
        refcount = 0;
        const_cast<Storage*>(this)->resources.clear();
#ifdef ETL_SELF_DELETING_SHARED_OBJECT
        delete this;
#endif
        return true;
    }

    return true;
}

int
Resource::Storage::count() const
{
#ifdef ETL_LOCK_REFCOUNTS
    etl::mutex::lock lock(mtx);
#endif
    return refcount;
}

void
Resource::set_alternative(Handle other) const
{
    assert(other);

    if (alternatives && other->alternatives) {
        if (alternatives != other->alternatives) {
            if (other->alternatives->resources.size() > alternatives->resources.size()) {
                other->set_alternative(Handle(const_cast<Resource*>(this)));
            } else {
                // merge
                Storage::Handle other_alternatives = other->alternatives;

                while (!other->alternatives->resources.empty()) {
                    alternatives->resources.push_back(
                        other->alternatives->resources.back());
                    other_alternatives->resources.pop_back();
                    other->alternatives = alternatives;
                }
            }
        }
    } else if (alternatives) {
        // add
        other->alternatives = alternatives;
        alternatives->resources.push_back(other);
    } else if (other->alternatives) {
        other->set_alternative(Handle(const_cast<Resource*>(this)));
    } else {
        // new storage
        alternatives = new Storage();
        alternatives->resources.push_back(Handle(const_cast<Resource*>(this)));
        alternatives->resources.push_back(other);
    }
}

void
Resource::unset_alternative() const
{
    if (alternatives) {
        List &resources = alternatives->resources;

        for (List::iterator i = resources.begin(); i != resources.end(); ++i)
            if (i->get() == this) {
                resources.erase(i);
                break;
            }

        alternatives.reset();
    }
}